//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Utils/Font/FontConverter.h"
#include "Shared/Resources/ResourceFileInfo.h"

#define _INC_STDDEF
#define _INC_STRING
#define _INC_STDLIB
#define _INC_ERRNO
#define _INC_STDIO

#include <ft2build.h>
#include <freetype/freetype.h>

#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_BITMAP_H
#include FT_TRUETYPE_TABLES_H


namespace Engine
{
namespace Utils
{

	// destructor
	FontConverter::~FontConverter ()
	{
		if ( _ftLibrary != null )
		{
			FT_Done_FreeType( (FT_Library) _ftLibrary );
			_ftLibrary = null;
		}
	}


	// LoadConfig
	bool FontConverter::LoadConfig (uni_c_string filename, uni_c_string outputPath, bool clearOutputDir)
	{
		static_obj_tree_t	cfg_tree;
		MetaFontList		obj;

		cfg_tree.Push( TConfigObjInfo( GetMetaClass().GetSetIter( obj ), null ) );
			
		const string	input_path = FileAddressUtils::GetPath( filename );

		_core->Config()->ClearDerectories();
		_core->Config()->AddDirectory( input_path );

		CHECK_ERR( _core->Parse( filename, cfg_tree ) );


		string	input_dir;
		string	output_dir;

		FileAddressUtils::BuildPath( input_dir, input_path, obj.inputDir );
		FileAddressUtils::BuildPath( output_dir, outputPath, obj.outputDir );
		
		// create output dir
		if ( not FileSystem::IsDirectoryExist( output_dir.cstr() ) )
		{
			FileSystem::NewDirectory( output_dir.cstr() );
		}

		if ( clearOutputDir )
		{
			_core->ClearDir( output_dir );
		}

		_charIntervals = obj.charIntervals;

		if ( _charIntervals.Empty() ) {
			_charIntervals.PushBack( uvec3( 33, 33, 95 ) );
		}

		FOR( i, obj.fonts )
		{
			string	in_fname;
			string	out_fname;

			FileAddressUtils::BuildPath( in_fname, input_dir, obj.fonts[i] );
			FileAddressUtils::BuildPath( out_fname, output_dir, obj.fonts[i], obj.extension );

			CHECK_ERR( _ConvertFont( in_fname, out_fname ) );
		}

		return true;
	}


	// _ConvertFont
	bool FontConverter::_ConvertFont (uni_c_string inputFile, uni_c_string outputFile)
	{
		FontParams	params;

		CHECK_ERR( _ReadParams( inputFile, 128, params ) );
		CHECK_ERR( _SaveFont( inputFile, outputFile, params ) );

		return true;
	}


	// _ReadParams
	bool FontConverter::_ReadParams (uni_c_string filename, uint size, OUT FontParams &params)
	{
		if ( _ftLibrary == null )
		{
			CHECK_ERR( FT_Init_FreeType( (FT_Library *) &_ftLibrary ) == 0 );
		}

		FT_Face		face = null;

		CHECK_ERR( FT_New_Face( (FT_Library) _ftLibrary, filename.cstr(), 0, &face ) == 0 );

		CHECK_ERR( FT_Set_Pixel_Sizes( face, size, size ) == 0 );
		CHECK_ERR( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) == 0 );
		
		int ascent  =  face->size->metrics.ascender >> 6;
		int descent = -face->size->metrics.descender >> 6;

		TT_OS2 * os2 = (TT_OS2 *) FT_Get_Sfnt_Table( face, ft_sfnt_os2 );

		if ( os2 != null )
		{
			ascent	= Max( ascent,  os2->usWinAscent  * face->size->metrics.y_ppem / face->units_per_EM );
			descent	= Max( descent, os2->usWinDescent * face->size->metrics.y_ppem / face->units_per_EM );

			ascent	= Max( ascent,   os2->sTypoAscender  * face->size->metrics.y_ppem / face->units_per_EM );
			descent	= Max( descent, -os2->sTypoDescender * face->size->metrics.y_ppem / face->units_per_EM );
		}

		const int	font_height = ascent + descent;
		
		const float	scale = 1.0f / font_height;

		float		base_line_top_margin = 0;
		int			max_top = 0;


		FOR( i, _charIntervals )
		{
			const uvec3	interv = _charIntervals[i];

			for (uint j = interv[0]; j < interv[2]; ++j)
			{
				uint16			symbol		= interv[1] + j;
				FT_UInt			char_index	= FT_Get_Char_Index( face, symbol );
				FT_GlyphSlot	slot		= face->glyph;
				
				CHECK_ERR( FT_Load_Glyph( face, char_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING /*FT_LOAD_FORCE_AUTOHINT*/ ) == 0 );

				base_line_top_margin = Max( slot->bitmap_top * scale, base_line_top_margin );

				max_top = Max( slot->bitmap_top, max_top );
			}
		}

		params.baseLineTopMargin	= base_line_top_margin;
		return true;
	}

	
	// _SaveFont
	bool FontConverter::_SaveFont (uni_c_string inputFile, uni_c_string outputFile, const FontParams &params)
	{
		// load font file
		RFilePtr	font_file;
		CHECK_ERR( (font_file = File::HddRFile::New( inputFile )).IsNotNull() );

		binary_array_t	buf;
		buf.Resize( font_file->Size(), false );

		CHECK_ERR( font_file->Read( buf.ptr(), buf.Size() ) );

		font_file = null;


		// save
		WFilePtr	file;
		CHECK_ERR( (file = File::HddWFile::New( outputFile )).IsNotNull() );

		RESFILE_WRITE_HEADER( file, FontFileInfo );

		CHECK_ERR( params.Serialize( file ) );

		CHECK_ERR( file->Write( buf.ptr(), buf.Size() ) );

		return true;
	}


}	// Utils
}	// Engine